package com;

import com.threegene.common2.web.exception.WebException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 请求帮助类
 *
 */
public class RequestHelper {

    public static final String UTF8 = "UTF-8";

    @Deprecated
    public static final String HEADER_REFERER = Headers.REFERER;

    @Deprecated
    public static final String HEADER_CONTENT_TYPE = Headers.CONTENT_TYPE;

    @Deprecated
    public static final String HEADER_ACCEPT = Headers.ACCEPT;

    @Deprecated
    public static final String HEADER_USER_AGENT = Headers.USER_AGENT;

    @Deprecated
    public static final String HEADER_X_REQUESTED_WITH = Headers.X_REQUESTED_WITH;

    @Deprecated
    public static final String HEADER_CONNECTION = Headers.CONNECTION;

    private static final String REQUEST_ATTR_BODY = "_REQUEST_BODY";

    private RequestHelper() {
        // nothing to do
    }

    /**
     * 获取body(安全)
     */
    public static String getBody(ServletRequest request) {
        String body = (String) request.getAttribute(REQUEST_ATTR_BODY);
        if (body != null) {
            return body;
        }
        body = readBody(request);
        request.setAttribute(REQUEST_ATTR_BODY, body);
        return body;
    }

    /**
     * 获取body(非安全)<br/>
     * 注意:request.getReader(),request.getInputStream()只能读取一次
     *
     * @deprecated See readBody(request)
     */
    @Deprecated
    public static String getBodyString(ServletRequest request) {
        return readBody(request);
    }

    /**
     * 读取body(非安全)<br/>
     * 注意:request.getReader(),request.getInputStream()只能读取一次
     *
     */
    public static String readBody(ServletRequest request) {
        StringBuilder sb = new StringBuilder();
        String inputLine;
        BufferedReader br = null;
        try {
            br = request.getReader();
            // br = new BufferedReader(new
            // InputStreamReader(request.getInputStream()));
            while ((inputLine = br.readLine()) != null) {
                sb.append(inputLine);
            }
        } catch (IOException e) {
            throw new WebException("Failed to read body.", e);
        } finally {
            Utils.close(br);
        }
        return sb.toString();
    }

    /**
     * 浏览器地址的URL
     */
    public static String getRawUrl(HttpServletRequest request) {
        StringBuilder url = getHostBuilder(request);
        url.append(request.getContextPath());
        url.append(request.getRequestURI());
        String queryString = request.getQueryString();
        if (queryString != null) {
            url.append("?" + queryString);
        }
        return url.toString();
    }

    /**
     * 获取相对URL（系统内），带参数。 HttpServletRequest.getServletPath()所获取的路径依赖入口定义
     *
     * @param request
     * @return
     */
    public static String getServletPath(HttpServletRequest request) {
        return getServletPath(request, false);
    }

    /**
     * 获取相对URL（系统内），带参数。 HttpServletRequest.getServletPath()所获取的路径依赖入口定义
     *
     * @param request
     * @param containContext
     *            是否包含容器部分路径
     * @return
     */
    public static String getServletPath(HttpServletRequest request, boolean containContext) {
        String url = request.getRequestURI();
        if (!containContext) {
            String context = request.getContextPath();
            if (!context.isEmpty()) {
                url = url.substring(context.length());
            }
        }
        String queryString = request.getQueryString();
        if (queryString != null && !queryString.isEmpty()) {
            url += "?" + queryString;
        }
        return url;
    }

    /**
     * 获取容器路径，带域名
     */
    public static String getContextPath(HttpServletRequest request) {
        StringBuilder sb = getHostBuilder(request);
        sb.append(request.getContextPath());
        return sb.toString();
    }

    public static String getHost(HttpServletRequest request) {
        return getHostBuilder(request).toString();
    }

    private static StringBuilder getHostBuilder(HttpServletRequest request) {
        StringBuilder url = new StringBuilder();
        url.append(request.getScheme() + "://");
        url.append(request.getServerName());
        int port = request.getServerPort();
        if (port != 80 && port != 443) {
            url.append(":").append(port);
        }
        return url;
    }

    public static String getRefererUrl(HttpServletRequest request) {
        return request.getHeader(Headers.REFERER);
    }

    /**
     * 获取访问IP <br/>
     * 在一般情况下使用Request.getRemoteAddr()即可，但是经过nginx等反向代理软件后，这个方法会失效。
     *
     */
    public static String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("X-Real-IP");
        if (Utils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Forwarded-For");
        } else {
            return ip;
        }
        if (Utils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        } else {
            // 多次反向代理后会有多个IP值，第一个为真实IP。
            int index = ip.indexOf(',');
            if (index != -1) {
                ip = ip.substring(0, index);
            }
        }
        return ip;
    }

    public static String getUserAgent(HttpServletRequest request) {
        return request.getHeader(Headers.USER_AGENT);
    }

    /**
     * 判断是否为Post请求
     */
    public static boolean isPostRequest(HttpServletRequest request) {
        return "POST".equalsIgnoreCase(request.getMethod());
    }

    /**
     * 判断是否为Get请求
     */
    public static boolean isGetRequest(HttpServletRequest request) {
        return "GET".equalsIgnoreCase(request.getMethod());
    }

    /**
     * 判断是否为JSON Post请求
     */
    public static boolean isJsonPostRequest(HttpServletRequest request) {
        if (!isPostRequest(request)) {
            return false;
        }
        return request.getContentType().toLowerCase().contains("application/json");
    }

    /**
     * 判断是否为Form请求
     */
    public static boolean isFormRequest(HttpServletRequest request) {
        String contentType = request.getContentType();
        return contentType != null && contentType.toLowerCase().startsWith("application/x-www-form-urlencoded");
    }

    /**
     * AJAX请求
     */
    public static boolean isAjaxRequest(HttpServletRequest request) {
        return "XMLHttpRequest".equalsIgnoreCase(request.getHeader(Headers.X_REQUESTED_WITH));
    }

    /**
     * Json请求
     */
    public static boolean isJsonRequest(HttpServletRequest request) {
        String type = request.getHeader(Headers.ACCEPT);
        if (Utils.isEmpty(type)) {
            return false;
        }
        return type.toLowerCase().contains("application/json");
    }

    /**
     * 根据浏览器If-Modified-Since Header, 计算文件是否已被修改.
     *
     * @param lastModifiedTime
     *            内容的最后修改时间.
     * @return false - 没修改，true - 有修改
     */
    public static boolean isModifiedSince(HttpServletRequest request, long lastModifiedTime) {
        long ifModifiedSince = request.getDateHeader(Headers.IF_MODIFIED_SINCE);
        if ((ifModifiedSince != -1) && (lastModifiedTime < ifModifiedSince + 1000)) {
            return false;
        }
        return true;
    }

    /**
     * 根据浏览器 If-None-Match Header, 计算Etag是否已无效.
     *
     * @param etag
     *            内容的ETag.
     * @return false - Etag没效，true - Etag有效
     */
    public static boolean isMatchEtag(HttpServletRequest request, String etag) {
        String headerValue = request.getHeader(Headers.IF_NONE_MATCH);
        if (headerValue != null) {
            boolean conditionSatisfied = false;
            if (!"*".equals(headerValue)) {
                StringTokenizer commaTokenizer = new StringTokenizer(headerValue, ",");
                while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) {
                    String currentToken = commaTokenizer.nextToken();
                    if (currentToken.trim().equals(etag)) {
                        return true;
                    }
                }
            } else {
                return true;
            }
        }
        return false;
    }

    /**
     * Forward跳转
     */
    public static void forward(String url, ServletRequest request, ServletResponse response) {
        RequestDispatcher rd = request.getRequestDispatcher(url);
        try {
            rd.forward(request, response);
        } catch (Exception e) {
            throw new WebException("Failed to forward. URL: " + url, e);
        }
    }

    /**
     * 获取请求头字符串
     */
    public static String getHeaderString(HttpServletRequest request) {
        StringBuilder sb = new StringBuilder(30);
        Enumeration<String> names = request.getHeaderNames();
        String name;
        Enumeration<String> values;
        String value;
        boolean isFirst;
        while (names.hasMoreElements()) {
            name = names.nextElement();
            sb.append(name).append(": ");
            values = request.getHeaders(name);
            isFirst = true;
            while (values.hasMoreElements()) {
                value = values.nextElement();
                if (!isFirst) {
                    sb.append("; ");
                }
                sb.append(value);
                isFirst = false;
            }
            sb.append("\n");
        }
        if (sb.length() == 0) {
            return Utils.EMPTY;
        }
        return sb.toString();
    }

    /**
     * 获取请求头map
     */
    public static Map<String, String> getHeaderMap(HttpServletRequest request) {
        Map<String, String> map = new HashMap<String, String>();
        Enumeration<String> names = request.getHeaderNames();
        String name;
        while (names.hasMoreElements()) {
            name = names.nextElement();
            map.put(name, request.getHeader(name));
        }
        return map;
    }

    /**
     * 获取参数字符串
     */
    public static String getParameterString(HttpServletRequest request) {
        if (isGetRequest(request)) {
            return request.getQueryString();
        }
        return getParameterString(request, "&", null);
    }

    /**
     * 获取参数字符串
     *
     * @param request
     * @param join
     *            连接符
     * @return
     */
    public static String getParameterString(HttpServletRequest request, String join) {
        return getParameterString(request, join, null);
    }

    /**
     * 获取参数字符串
     *
     * @param request
     * @param join
     *            连接符
     * @param charset
     *            编码
     * @return
     */
    public static String getParameterString(HttpServletRequest request, String join, String charset) {

        Map<String, String[]> map = request.getParameterMap();
        if (map.isEmpty()) {
            return Utils.EMPTY;
        }
        StringBuilder sb = new StringBuilder();
        String val;
        if (Utils.isEmpty(charset)) {
            for (String key : map.keySet()) {
                val = request.getParameter(key);
                if (Utils.isEmpty(val)) {
                    continue;
                }
                sb.append(join).append(key).append("=").append(val);
            }
        } else {
            for (String key : map.keySet()) {
                val = request.getParameter(key);
                if (Utils.isEmpty(val)) {
                    continue;
                }
                sb.append(join).append(encodeUrl(key, charset)).append("=").append(encodeUrl(val, charset));
            }
        }
        return sb.length() == 0 ? Utils.EMPTY : sb.substring(join.length());
    }

    /**
     * 获取URL参数，忽略大小写
     */
    public static String getParameterIgnoreCase(HttpServletRequest request, String paramName) {
        return getParameterIgnoreCase(request, paramName, null);
    }

    /**
     * 获取URL参数，忽略大小写
     */
    public static String getParameterIgnoreCase(HttpServletRequest request, String paramName, String defaultValue) {
        Enumeration<?> enu = request.getParameterNames();
        String key;
        while (enu.hasMoreElements()) {
            key = (String) enu.nextElement();
            if (paramName.equalsIgnoreCase(key)) {
                return request.getParameter(key);
            }
        }
        return defaultValue;
    }

    public static String getQueryString(String url) {
        int index = url.trim().indexOf("?");
        if (index == -1) {
            return null;
        }
        return url.substring(index + 1).trim();
    }

    public static String buildQueryString(Map<String, String> parameterMap) {
        if (parameterMap == null || parameterMap.isEmpty()) {
            return null;
        }
        StringBuilder query = new StringBuilder();
        for (Map.Entry<String, String> entry : parameterMap.entrySet()) {
            query.append("&").append(entry.getKey()).append("=").append(entry.getValue());
        }
        if (query.length() > 0) {
            return query.substring(1);
        } else {
            return null;
        }
    }

    public static Map<String, String> getParameterMap(String queryString) {
        if (queryString == null || queryString.isEmpty()) {
            return null;
        }
        Map<String, String> parameterMap = new HashMap<String, String>();
        String[] parmsArr = queryString.split("&");
        for (String parmString : parmsArr) {
            if (parmString.trim().length() == 0) {
                continue;
            }
            String[] keyValue = parmString.split("=");
            parameterMap.put(keyValue[0].trim(), (keyValue.length > 1 ? keyValue[1] : null));
        }
        return parameterMap;
    }

    /**
     * 设置URL参数.参数名称区分大小写
     *
     * @param url
     * @param key
     *            参数名称
     * @param value
     *            参数值
     * @return
     */
    public static String setUrlParameter(String url, String key, String value) {
        int index = url.indexOf("?");
        if (index < 0) {
            return url + "?" + key + "=" + value;
        } else if (index == (url.length() - 1)) {
            return url + key + "=" + value;
        }
        String path = url.substring(0, index + 1);
        String queryString = url.substring(index + 1) + "&";
        Pattern pattern = Pattern.compile(key + "=.*?&");
        Matcher matcher = pattern.matcher(queryString);
        if (matcher.find()) {
            queryString = matcher.replaceAll(key + "=" + value + "&");
            if (queryString.endsWith("&")) {
                queryString = queryString.substring(0, queryString.length() - 1);
            }
            return path + queryString;
        } else {
            return url + "&" + key + "=" + value;
        }
    }

    /**
     * 删除URL参数.参数名称区分大小写
     *
     * @param url
     * @param key
     * @return
     */
    public static String removeUrlParameter(String url, String key) {
        int index = url.indexOf("?");
        if (index < 0) {
            return url;
        } else if (index == (url.length() - 1)) {
            return url.substring(0, url.length() - 1);
        }
        String path = url.substring(0, index);
        String queryString = url.substring(index + 1) + "&";
        Pattern pattern = Pattern.compile(key + "=.*?&");
        Matcher matcher = pattern.matcher(queryString);
        if (matcher.find()) {
            queryString = matcher.replaceAll("");
            if (queryString.endsWith("&")) {
                queryString = queryString.substring(0, queryString.length() - 1);
            }
            if (queryString.length() > 0) {
                return path + "?" + queryString;
            } else {
                return path;
            }
        }
        return url;
    }

    /**
     * 删除URL
     *
     * @param url
     * @return
     */
    public static String removeQueryString(String url) {
        int index = url.indexOf('?');
        if (index > -1) {
            return url.substring(0, index);
        }
        return url;
    }

    /**
     * 合并URL参数串
     */
    public static String mergeQueryString(String url, String queryString) {
        return url + (url.indexOf("?") > -1 ? "&" : "?") + queryString;
    }

    /**
     * 合并URL参数串
     *
     * @param url
     * @param query
     * @return
     */
    public static String mergeQueryString(String url, String... query) {
        StringBuilder quertyBuilder = new StringBuilder();
        for (String q : query) {
            if (q != null && q.length() > 0) {
                quertyBuilder.append("&").append(q);
            }
        }
        if (quertyBuilder.length() > 0) {
            if (url.indexOf("?") < 0) {
                quertyBuilder.replace(0, 1, "?");
            }
            return url + quertyBuilder.toString();
        } else {
            return url;
        }
    }

    /**
     * URL编码（UTF-8）
     */
    public static String encodeUrl(String url) {
        return encodeUrl(url, UTF8);
    }

    /**
     * URL编码
     */
    public static String encodeUrl(String url, String charset) {
        if (url == null || url.isEmpty()) {
            return url;
        }
        try {
            return URLEncoder.encode(url, charset);
        } catch (UnsupportedEncodingException e) {
            return url;
        }
    }

    public static String decodeUrl(String url) {
        return decodeUrl(url, UTF8);
    }

    public static String decodeUrl(String url, String charset) {
        if (url == null || url.isEmpty()) {
            return url;
        }
        try {
            return URLDecoder.decode(url, charset);
        } catch (UnsupportedEncodingException e) {
            return url;
        }
    }

    /**
     * 获取字符串，消除前后空格
     */
    public static String paramTrim(HttpServletRequest request, String name, String defaultValue) {
        String s = request.getParameter(name);
        return s == null ? defaultValue : s.trim();
    }

    /**
     * 获取int类型参数
     */
    public static Integer paramAsInt(HttpServletRequest request, String name, Integer defaultValue) {
        String s = request.getParameter(name);
        if (s == null || s.isEmpty()) {
            return defaultValue;
        }
        try {
            return Integer.parseInt(s);
        } catch (Exception e) {
            return defaultValue;
        }
    }

    /**
     * 获取long类型参数
     */
    public static Long paramAsLong(HttpServletRequest request, String name, Long defaultValue) {
        String s = request.getParameter(name);
        if (s == null || s.isEmpty()) {
            return defaultValue;
        }
        try {
            return Long.parseLong(s);
        } catch (Exception e) {
            return defaultValue;
        }
    }

    /**
     * 获取 Double类型参数
     */
    public static Double paramAsDouble(HttpServletRequest request, String name, Double defaultValue) {
        String s = request.getParameter(name);
        if (s == null || s.isEmpty()) {
            return defaultValue;
        }
        try {
            return Double.parseDouble(s);
        } catch (Exception e) {
            return defaultValue;
        }
    }

    /**
     * 获取bool类型参数
     */
    public static Boolean paramAsBool(HttpServletRequest request, String name, Boolean defaultValue) {
        String s = request.getParameter(name);
        if (s == null || s.isEmpty()) {
            return defaultValue;
        }
        if (s.equals("1") || s.equalsIgnoreCase("true")) {
            return true;
        }
        if (s.equals("0") || s.equalsIgnoreCase("false")) {
            return false;
        }
        return defaultValue;
    }

    /**
     * 获取日期参数
     */
    public static Date paramAsDate(HttpServletRequest request, String name, Date defaultValue, DateFormat... formats) {
        if (formats == null || formats.length == 0) {
            throw new IllegalArgumentException("invalid formats");
        }
        String s = paramTrim(request, name, null);
        if (Utils.isEmpty(s)) {
            return defaultValue;
        }
        Date date = Utils.parseDate(s, formats);
        return date == null ? defaultValue : date;
    }

    /**
     * 获取日期参数
     *
     * @param request
     * @param name
     * @param defaultValue
     * @param formats
     *            格式。如yyyy-MM-dd HH:mm:ss
     * @return
     */
    public static Date paramAsDate(HttpServletRequest request, String name, Date defaultValue, String... formats) {
        if (formats == null || formats.length == 0) {
            throw new IllegalArgumentException("invalid formats");
        }
        String s = paramTrim(request, name, null);
        if (Utils.isEmpty(s)) {
            return defaultValue;
        }
        Date date = Utils.parseDate(s, formats);
        return date == null ? defaultValue : date;
    }

    /**
     * 获取URL参数，忽略大小写
     */
    public static String paramIgnoreCase(HttpServletRequest request, String paramName) {
        return paramIgnoreCase(request, paramName, null);
    }

    /**
     * 获取URL参数，忽略大小写
     */
    public static String paramIgnoreCase(HttpServletRequest request, String paramName, String defaultValue) {
        Enumeration<?> enu = request.getParameterNames();
        String key;
        while (enu.hasMoreElements()) {
            key = (String) enu.nextElement();
            if (paramName.equalsIgnoreCase(key)) {
                return request.getParameter(key);
            }
        }
        return defaultValue;
    }

}
